package com.xiguaji.tiktok.task.ranking.event;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.gugee.tiktok.common.model.dal.blogger.BloggerTypeRepository;
import com.gugee.tiktok.common.model.mysql.BloggerMain;
import com.gugee.tiktok.common.model.mysql.BloggerRecord;
import com.gugee.tiktok.common.model.mysql.BloggerType;
import com.gugee.tiktok.common.model.ranking.RankingFollowers;
import com.xiguaji.boot.eventbus.Event;
import com.xiguaji.boot.eventbus.IEventListener;
import com.xiguaji.tiktok.task.ranking.RankingFollowerService;
import com.xiguaji.tiktok.task.ranking.client.BloggerClient;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;

@Component
@Slf4j
public class FetchBloggerRankingFollowerEventListener implements IEventListener<FetchBloggerRankingFollowerEventListener.FetchBloggerRankingFollowerEvent> {

    @Autowired
    RankingFollowerService rankingFollowerService;

    @Autowired
    BloggerTypeRepository bloggerTypeRepository;

    @Resource(name ="BloggerClientRanking")
    BloggerClient bloggerClient;

    List<BloggerType> bloggerTypes = new ArrayList<>();

    @PostConstruct
    public void initConstruct(){
        BloggerType bloggerType = new BloggerType();
        bloggerType.setId(0);
        bloggerType.setName("ALL");
        this.bloggerTypes.add(bloggerType);
        this.bloggerTypes.addAll(bloggerTypeRepository.getBloggerTypeByIsDisplay(true));
    }

    @Override
    @Subscribe
    @AllowConcurrentEvents
    public void handle(FetchBloggerRankingFollowerEvent event) {
        Calendar calendar = Calendar.getInstance();

        // calendar.add(Calendar.DAY_OF_MONTH,-1);
       bloggerTypes.parallelStream().forEach(bloggerType -> {
            try{
                dayRanking(bloggerType,calendar);
            }catch (Exception e){
                log.error("<<<<<<博主粉丝日榜,{}分类出错",bloggerType.getName(),e);
            }
        });
        // 周统计
        if(calendar.get(Calendar.DAY_OF_WEEK) == 1){
            bloggerTypes.parallelStream().forEach(bloggerType -> {
                try{
                    weekRanking(bloggerType,calendar);
                }catch (Exception e){
                    log.error("<<<<<<博主粉丝周榜,{}分类出错",bloggerType.getName(),e);
                }
            });
        }
        // 月统计
        if(calendar.get(Calendar.DAY_OF_MONTH) == 1){
            bloggerTypes.parallelStream().forEach(bloggerType -> {
                try{
                    monthRanking(bloggerType,calendar);
                }catch (Exception e){
                    log.error("<<<<<<博主粉丝月榜,{}分类出错",bloggerType.getName(),e);
                }
            });
        }


    }


    /**
     * 每天排行榜
     * @param bloggerType
     */
    public void dayRanking(BloggerType bloggerType,Calendar date){
        int dateCode = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
        long rankingStart = System.currentTimeMillis();
        List<BloggerRecord> bloggerRecords = bloggerClient.findBloggerRecords(dateCode,bloggerType.getName().equals("ALL")?"":bloggerType.getName());
        if((System.currentTimeMillis()-rankingStart)/1000 >1)
            log.info("FetchBloggerRankingFollowerEventListener rankingType:"+bloggerType.getName()+" find use time:"+(System.currentTimeMillis()-rankingStart)/1000+"s");

        if(bloggerRecords.size() >0){
            int beforeDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,2),"yyyyMMdd"));
            int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));

            List<RankingFollowers> beforeRankings = rankingFollowerService.findRankingFollowers(bloggerType.getId(),1,beforeDay);
            Map<Long,RankingFollowers> beforeRankingMap = new HashMap<>();

            beforeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<RankingFollowers> rankings = new ArrayList<>();

            int rankingSort = 0;
            for (int i = 0; i < bloggerRecords.size(); i++) {
                try{
                    BloggerMain bloggerMain = bloggerClient.getBloggerByUid(bloggerRecords.get(i).getUid());
                    RankingFollowers ranking = new RankingFollowers();
                    ranking.setUid(bloggerRecords.get(i).getUid());
                    ranking.setBloggerId(bloggerRecords.get(i).getBloggerId());
                    ranking.setBloggerType(bloggerType.getId());
                    ranking.setNickname(bloggerRecords.get(i).getNickname());
                    ranking.setUniqueId(bloggerRecords.get(i).getUniqueId());
                    ranking.setCustomVerify(bloggerMain.getCustomVerify());
                    ranking.setEnterpriseVerifyReason(bloggerMain.getEnterpriseVerifyReason());
                    ranking.setRegion(bloggerMain.getORegion());
                    ranking.setFollower((int)bloggerRecords.get(i).getFollowerCount());
                    ranking.setAvatar(bloggerRecords.get(i).getAvatar());
                    ranking.setRankingType(1);
                    ranking.setBelong(curDay);
                    ranking.setFollowerGrowth((int)bloggerRecords.get(i).getFollowerGrowth());
                    ranking.setRanking(rankingSort+1);
                    var beforeDayRanking = beforeRankingMap.get(bloggerRecords.get(i).getUid());

                    //参与率
                    try{
                        BigDecimal dscCount = new BigDecimal(bloggerMain.getOAvgLikes()
                                +bloggerMain.getOAvgShare()+
                                bloggerMain.getOAvgReviews());
                        ranking.setEngagementRate((dscCount.divide(new BigDecimal(bloggerMain.getOAvgViews()),3, RoundingMode.FLOOR).multiply(new BigDecimal(100))).setScale(1).doubleValue());

                    }catch (Exception e){
                        ranking.setEngagementRate(0.0);
                    }

                    if(null != beforeDayRanking){

                        // 排除前一天没更新，第二天疯涨粉的
                        if(beforeDayRanking.getFollowerGrowth() == 0 ){
                            continue;
                        }

                        if(ranking.getRanking() < beforeDayRanking.getRanking()){
                            ranking.setState(State.UP.getValue());
                        }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                            ranking.setState(State.DOWN.getValue());
                        }else{
                            ranking.setState(State.FLAT.getValue());
                        }

                        ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                    }else{
                        ranking.setPosition(0);
                        ranking.setState(State.NEW.getValue());
                    }
                    ranking.setCreateBy("FetchBloggerRankingFollower - Day -RankingFollower");
                    ranking.setUpdateBy("FetchBloggerRankingFollowerEvent - Day -RankingFollower");
                    rankingSort+=1;
                    rankings.add(ranking);
                }catch (Exception e){
                    log.error("FetchBloggerRankingFollower error uid:{}",bloggerRecords.get(i).getUid(),e);
                    continue;
                }
            }

            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingFollowerService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingFollowerEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    public void weekRanking(BloggerType bloggerType,Calendar date){
        int beforeDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,7),"yyyyMMdd"));
        int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
        // 当前周排行数据（上周）
        List<RankingFollowers> curRankings = rankingFollowerService.findRankingFollowers(bloggerType.getId(),1,beforeDay,curDay);

        if(curRankings.size() > 0) {
            // 上周排行数据（上上周）
            List<RankingFollowers> lastTimeRankings = rankingFollowerService.findRankingFollowers(bloggerType.getId(),
                    2,Integer.parseInt(DateUtil.format(getBeforeDay(date,14),"yyyyMMdd")));
            Map<Long,RankingFollowers> beforeRankingMap = new HashMap<>();

            lastTimeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<RankingFollowers> rankings = new ArrayList<>();

            for (int i = 0; i < curRankings.size(); i++) {
                RankingFollowers ranking = curRankings.get(i);
                ranking.setRankingType(2);
                ranking.setBelong(beforeDay);
                ranking.setRanking(i+1);
                ranking.setFollower(ranking.getFollower());
                var beforeDayRanking = beforeRankingMap.get(ranking.getUid());

                if(null != beforeDayRanking){
                    //ranking.setFollowerGrowth(ranking.getFollowerGrowth()-beforeDayRanking.getFollowerGrowth());
                    if(ranking.getRanking() < beforeDayRanking.getRanking()){
                        ranking.setState(State.UP.getValue());
                    }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                        ranking.setState(State.DOWN.getValue());
                    }else{
                        ranking.setState(State.FLAT.getValue());
                    }

                    ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                }else{
                    ranking.setPosition(0);
                    ranking.setState(State.NEW.getValue());
                }
                ranking.setCreateBy("FetchBloggerRankingFollower - Week -RankingFollower");
                ranking.setUpdateBy("FetchBloggerRankingFollower - Week -RankingFollower");
                ranking.setId(0);
                rankings.add(ranking);
            }
            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingFollowerService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingFollowerEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    public void monthRanking(BloggerType bloggerType,Calendar date){
        int beforeDay = Integer.parseInt(DateUtil.format(getBeforeMonth(date,1),"yyyyMMdd"));
        int curDay = Integer.parseInt(DateUtil.format(getBeforeDay(date,1),"yyyyMMdd"));
        // 当前月排行数据
        List<RankingFollowers> curRankings = rankingFollowerService.findRankingFollowers(bloggerType.getId(),1,beforeDay,curDay);

        if(curRankings.size() > 0) {
            // 上月排行数据
            List<RankingFollowers> lastTimeRankings = rankingFollowerService.findRankingFollowers(bloggerType.getId(),
                    3,Integer.parseInt(DateUtil.format(getBeforeMonth(date,2),"yyyyMMdd")));
            Map<Long,RankingFollowers> beforeRankingMap = new HashMap<>();

            lastTimeRankings.forEach(b->beforeRankingMap.put(b.getUid(),b));
            List<RankingFollowers> rankings = new ArrayList<>();

            for (int i = 0; i < curRankings.size(); i++) {
                RankingFollowers ranking = curRankings.get(i);
                ranking.setRankingType(3);
                ranking.setBelong(beforeDay);
                ranking.setRanking(i+1);
                var beforeDayRanking = beforeRankingMap.get(ranking.getUid());

                if(null != beforeDayRanking){
                    // ranking.setFollowerGrowth(ranking.getFollowerGrowth()-beforeDayRanking.getFollowerGrowth());

                    if(ranking.getRanking() < beforeDayRanking.getRanking()){
                        ranking.setState(State.UP.getValue());
                    }else if(ranking.getRanking() > beforeDayRanking.getRanking()){
                        ranking.setState(State.DOWN.getValue());
                    }else{
                        ranking.setState(State.FLAT.getValue());
                    }
                    ranking.setPosition(beforeDayRanking.getRanking() - ranking.getRanking());
                }else{
                    ranking.setPosition(0);
                    ranking.setState(State.NEW.getValue());
                }
                ranking.setCreateBy("FetchBloggerRankingFollower - Month -RankingFollower");
                ranking.setUpdateBy("FetchBloggerRankingFollower - Month -RankingFollower");
                ranking.setId(0);
                rankings.add(ranking);
            }
            if(rankings.size() > 0){
                long rankingSaveStart = System.currentTimeMillis();
                rankingFollowerService.saveAll(rankings);
                if((System.currentTimeMillis()-rankingSaveStart)/1000 >1)
                    log.info("FetchBloggerRankingFollowerEventListener rankingType:"+bloggerType.getName()+" save use time:"+(System.currentTimeMillis()-rankingSaveStart)/1000+"s");
            }
        }
    }

    private Date getBeforeDay(Calendar calendar,int day){
        Calendar now = ObjectUtil.clone(calendar);
        now.add(Calendar.DAY_OF_MONTH,-day);
        return now.getTime();
    }

    private Date getBeforeMonth(Calendar calendar,int month){
        Calendar now = ObjectUtil.clone(calendar);
        now.add(Calendar.MONTH,-month);
        return now.getTime();
    }

    private enum State {
        UP("UP"),
        DOWN("DOWN"),
        FLAT("FLAT"),
        NEW("NEW");

        State(String value){
            this.value = value;
        }
        private String value;

        public String getValue() {
            return value;
        }
    }

    public static class FetchBloggerRankingFollowerEvent extends Event{

        public FetchBloggerRankingFollowerEvent() {
            super("FetchBloggerRanking");
        }
    }
}
